using System;
using System.Collections.Specialized;
using System.Text;
using System.Web.Security;
using System.Web.Hosting;
using Emerald.Business;
using Emerald.Business.Controllers;
using System.Configuration.Provider;
using System.Linq;

namespace Emerald.WebSite.Extensions.Security
{
    /// <summary>
    /// The Emerald role provider.
    /// </summary>
    public class EmeraldRoleProvider : RoleProvider
    {
        private UserController m_userController;
        private RoleController m_roleController;
        private string m_applicationName;

        #region Private Properties

        private UserController UserController
        {
            get
            {
                if (m_userController == null)
                {
                    m_userController = new UserController();
                }

                return m_userController;
            }
        }

        private RoleController RoleController
        {
            get
            {
                if (m_roleController == null)
                {
                    m_roleController = new RoleController();
                }

                return m_roleController;
            }
        }

        #endregion

        #region Private Methods

        private bool RoleExists(string roleName, out Role role)
        {
            #region Validation
            if (String.IsNullOrEmpty(roleName))
            {
                throw new ArgumentNullException("roleName");
            }
            #endregion

            role = RoleController.GetRoleByRoleName(roleName);

            return role != null;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="config"></param>
        public override void Initialize(string name, NameValueCollection config)
        {
            #region Validation
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            #endregion

            if (name == null || name.Length == 0)
            {
                name = "Emerald";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Emerald role provider");
            }

            m_applicationName = ProviderHelper.GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);

            base.Initialize(name, config);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="usernames"></param>
        /// <param name="roleNames"></param>
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            #region Validation
            if (usernames == null)
            {
                throw new ArgumentNullException("usernames");
            }

            if (roleNames == null)
            {
                throw new ArgumentNullException("roleNames");
            }
            #endregion

            foreach (string username in usernames)
            {
                foreach (string roleName in roleNames)
                {
                    Role role;

                    if (!RoleExists(roleName, out role))
                    {
                        throw new ProviderException("Role name not found.");
                    }

                    if (IsUserInRole(username, roleName))
                    {
                        throw new ProviderException("User is already in role.");
                    }

                    User user = UserController.GetUserByUsername(username);

                    if (user == null)
                    {
                        throw new ProviderException("User not found.");
                    }

                    user.Roles.Add(role);
                    UserController.Update(user, false);
                }
            }

            UserController.SubmitChanges();
        }

        /// <summary>
        /// 
        /// </summary>
        public override string ApplicationName
        {
            get { return m_applicationName; }
            set { m_applicationName = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="roleName"></param>
        public override void CreateRole(string roleName)
        {
            #region Validation
            if (String.IsNullOrEmpty(roleName))
            {
                throw new ArgumentNullException("roleName");
            }
            #endregion

            if (RoleExists(roleName))
            {
                throw new ProviderException("Role name already exists.");
            }

            Role role = new Role();
            role.RoleName = roleName;

            RoleController.Insert(role);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="roleName"></param>
        /// <param name="throwOnPopulatedRole"></param>
        /// <returns></returns>
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            #region Validation
            if (String.IsNullOrEmpty(roleName))
            {
                throw new ArgumentNullException("roleName");
            }
            #endregion

            Role role;

            if (!RoleExists(roleName, out role))
            {
                throw new ProviderException("Role name not found.");
            }

            if (throwOnPopulatedRole)
            {
                if (role.Users.Count > 0)
                {
                    throw new ProviderException("Cannot delete a populated role");
                }
            }

            RoleController.Delete(role);

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="roleName"></param>
        /// <param name="usernameToMatch"></param>
        /// <returns></returns>
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            #region Validation
            if (String.IsNullOrEmpty(roleName))
            {
                throw new ArgumentNullException("roleName");
            }

            if (String.IsNullOrEmpty(usernameToMatch))
            {
                throw new ArgumentNullException("usernameToMatch");
            }
            #endregion

            Role role;

            if (!RoleExists(roleName, out role))
            {
                throw new ProviderException("Role name not found.");
            }

            string[] result = role.Users.Select(u => u.Username).ToArray();

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string[] GetAllRoles()
        {
            string[] result = RoleController.SelectAll().Select(role => role.RoleName).ToArray();

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public override string[] GetRolesForUser(string username)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }
            #endregion

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new ProviderException("User not found.");
            }

            string[] result = user.Roles.Select(role => role.RoleName).ToArray();

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="roleName"></param>
        /// <returns></returns>
        public override string[] GetUsersInRole(string roleName)
        {
            #region Validation
            if (String.IsNullOrEmpty(roleName))
            {
                throw new ArgumentNullException("roleName");
            }
            #endregion

            Role role;

            if (!RoleExists(roleName, out role))
            {
                throw new ProviderException("Role name not found.");
            }

            string[] result = role.Users.Select(user => user.Username).ToArray();

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="username"></param>
        /// <param name="roleName"></param>
        /// <returns></returns>
        public override bool IsUserInRole(string username, string roleName)
        {
            #region Validation
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }

            if (String.IsNullOrEmpty(roleName))
            {
                throw new ArgumentNullException("roleName");
            }
            #endregion

            User user = UserController.GetUserByUsername(username);

            if (user == null)
            {
                throw new ProviderException("User not found.");
            }

            bool result = user.Roles.Any(role => role.RoleName == roleName);

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="usernames"></param>
        /// <param name="roleNames"></param>
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            #region Validation
            if (usernames == null)
            {
                throw new ArgumentNullException("usernames");
            }

            if (roleNames == null)
            {
                throw new ArgumentNullException("roleNames");
            }
            #endregion

            foreach (string username in usernames)
            {
                foreach (string roleName in roleNames)
                {
                    Role role;

                    if (!RoleExists(roleName, out role))
                    {
                        throw new ProviderException("Role name not found.");
                    }

                    if (!IsUserInRole(username, roleName))
                    {
                        throw new ProviderException("User is not this in role.");
                    }

                    User user = UserController.GetUserByUsername(username);

                    if (user == null)
                    {
                        throw new ProviderException("User not found.");
                    }

                    user.Roles.Remove(role);
                    UserController.Update(user, false);
                }
            }

            UserController.SubmitChanges();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="roleName"></param>
        /// <returns></returns>
        public override bool RoleExists(string roleName)
        {
            #region Validation
            if (String.IsNullOrEmpty(roleName))
            {
                throw new ArgumentNullException("roleName");
            }
            #endregion

            Role role;

            return RoleExists(roleName, out role);
        }

        #endregion
    }
}